﻿using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;

namespace ProjectTW.Foliage
{
    public static class GPUInstancingCore
    {
        /// <summary>
        /// GPU-Instancing最大不能超过1023
        /// </summary>
        public const int Max_Instancing_Amount = 1023;

        /// <summary>
        /// 创建Mesh对应的实例栈
        /// </summary>
        /// <param name="foliageGpuMesh"></param>
        /// <returns></returns>
        public static Dictionary<Mesh, GPUInstancingStack> CreateInstancingStack(FoliageGPUMesh foliageGpuMesh)
        {
            Dictionary<Mesh, GPUInstancingStack> stackInstance = new Dictionary<Mesh, GPUInstancingStack>();

            for (int a = 0; a < foliageGpuMesh.MeshLODsCount; a++)
            {
                for (int b = 0; b < foliageGpuMesh.DensityLODsCount; b++)
                {
                    for (int c = 0; c < foliageGpuMesh.MeshesCount; c++)
                    {
                        stackInstance.Add(foliageGpuMesh.MeshesCache[a, b, c], new GPUInstancingStack());
                    }
                }
            }

            return stackInstance;
        }

        /// <summary>
        /// 重置Mesh对应的实例栈
        /// </summary>
        /// <param name="stack"></param>
        /// <param name="foliageGpuMesh"></param>
        public static void ResetInstancingStack(Dictionary<Mesh, GPUInstancingStack> stack, FoliageGPUMesh foliageGpuMesh)
        {
            if (stack == null)
            {
                return;
            }

            for (int a = 0; a < foliageGpuMesh.MeshLODsCount; a++)
            {
                for (int b = 0; b < foliageGpuMesh.DensityLODsCount; b++)
                {
                    for (int c = 0; c < foliageGpuMesh.MeshesCount; c++)
                    {
                        stack[foliageGpuMesh.MeshesCache[a, b, c]].Clear();
                    }
                }
            }
        }

        /// <summary>
        /// 渲染实例栈
        /// </summary>
        /// <param name="stacks"></param>
        /// <param name="foliageGpuMesh"></param>
        /// <param name="prototype"></param>
        /// <param name="propertyBlock"></param>
        /// <param name="camera"></param>
        public static void RenderInstancingStack(Dictionary<Mesh, GPUInstancingStack> stacks, FoliageGPUMesh foliageGpuMesh, FoliagePrototype prototype, MaterialPropertyBlock propertyBlock,
                                                 Camera camera)
        {
            if (stacks == null)
            {
                return;
            }

            Mesh mesh;
            // 原型对应参数
            Material mat = prototype.FoliageMesh.Mat;
            GPUInstancingStack stack;
            // 阴影模型
            ShadowCastingMode castShadows = prototype.CastShadows ? ShadowCastingMode.On : ShadowCastingMode.Off;
            bool receiveShadows = prototype.ReceiveShadows;

            for (int a = 0; a < foliageGpuMesh.MeshLODsCount; a++)
            {
                for (int b = 0; b < foliageGpuMesh.DensityLODsCount; b++)
                {
                    for (int c = 0; c < foliageGpuMesh.MeshesCount; c++)
                    {
                        mesh = foliageGpuMesh.MeshesCache[a, b, c];
                        stack = stacks[mesh];

                        if (stack.MatrixStash.Count <= 0)
                        {
                            continue;
                        }

                        propertyBlock.SetVectorArray(FoliageMeshManager.PropertyIdWorldposition, stack.VectorStash);

                        Graphics.DrawMeshInstanced(mesh, 0, mat, stacks[mesh].MatrixStash, propertyBlock, castShadows, receiveShadows, prototype.RenderingLayer,
                                RenderingQueueMeshInstanceSimulator.SettingsIsPlaying ? camera : null);
                    }
                }
            }
        }
    }
}
